package com.minicat.server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Modifier;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

public class MainStart {

    private int port = 8071;

    private String hostName = "localhost";

    private String appBase = "";

    private Map<String,HttpServlet> servletMap = new HashMap<>(16);

    private Mapper mapper = new Mapper();

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }
    /**
     * miniact 4.0
     * 1、解析server.xml文件
     * 2、按照其中的属性读取webapps路径&设置服务器监听的端口号
     * 3、扫码webapps下面的web.xml文件对url-patten和servlet对应关系进行映射
     * 4、启动服务接收请求
     */
    public void start() throws Exception {
        parseServerXml();
        setMapper();
        int corePoolSize = 3;
        int maximumPoolSize = 10;
        long keepAliveTime = 30L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime, unit,queue,threadFactory,handler);
        ServerSocket serverSocket = new ServerSocket(port);

        while (true){
            System.out.println("minicatV4.0 accept request start.");
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, mapper);
            threadPoolExecutor.execute(requestProcessor);
        }
    }
    //解析server.xml文件
    public void parseServerXml(){
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> selectNodes = rootElement.selectNodes("//Server");
            for (int i = 0;i < selectNodes.size();i++){
                Element element = selectNodes.get(i);
                List<Element> serviceList = element.selectNodes("//Service");
                for (Element service : serviceList) {
                    Element connector = (Element) service.selectSingleNode("//Connector");
                    if (connector.attributeValue("port") != null) {
                        port = Integer.parseInt(connector.attributeValue("port"));
                    }
                    List<Element> engineList = connector.selectNodes("//Engine");
                    for (Element engine : engineList) {
                        List<Element> hostList =  engine.selectNodes("//Host");
                        for (Element host : hostList) {
                            if (host.attributeValue("name") != null){
                                hostName = host.attributeValue("name");
                            }
                            if (host.attributeValue("appBase") != null){
                                appBase = host.attributeValue("appBase");
                            }
                            Host h = new Host();
                            h.setAppBase(appBase);
                            h.setName(hostName);
                            mapper.getHosts().add(h);
                        }
                    }
                }
                System.out.println("port:"+port+",hostName:"+hostName+",appBase:"+appBase);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void setMapper() {
        for (Host host : mapper.getHosts()) {
            setHostContext(host,host.getAppBase());
        }
    }

    /**
     * 构建 /demo1/lagou -> LagouServlet的对应关系
     * @param host
     * @param appBase
     */
    private void setHostContext(Host host,String appBase){
        //C:\work\java\stage02_module01_MinicatV4.0\webapps
        File f = new File(appBase);
        File[] files = f.listFiles();
        if (files == null){
            return;
        }
        for (File file : files) {
            /**
             * C:\work\java\stage02_module01_MinicatV4.0\webapps\demo1
             * C:\work\java\stage02_module01_MinicatV4.0\webapps\demo2
             */
            if (file.isDirectory()){
                //如果是根目录下,封装Context
                //获取webapp根目录下的文件夹名
                String name = file.getName();
                Context context = new Context();
                context.setName(name);
                host.getContexts().add(context);
            }
        }
        loadServlet(host);
    }

    //解析web.xml文件,将url-pattern和servlet-class进行对应
    public void loadServlet(Host host) {
        String webxmlPath = "";
        for (Context context : host.getContexts()) {
            webxmlPath = host.getAppBase()+"/"+context.getName()+"/web.xml";
            File f = new File(webxmlPath);
            if (!f.exists() || !f.isFile()){
                throw new RuntimeException(webxmlPath+" file not found.");
            }
            try{
                SAXReader saxReader = new SAXReader();
                Document document = saxReader.read(new File(webxmlPath));
                Element rootElement = document.getRootElement();
                List<Element> selectNodes = rootElement.selectNodes("//servlet");
                for (int i = 0; i < selectNodes.size(); i++) {
                    Element element = selectNodes.get(i);
                    //<servlet-name>minicat</servlet-name>
                    Node servletNameNode = element.selectSingleNode("servlet-name");
                    String servletName = servletNameNode.getText();
                    //<servlet-class>com.minicat.server.HttpServlet</servlet-class>
                    Node servletClassNode = element.selectSingleNode("servlet-class");
                    String servletClass = servletClassNode.getText();
                    //根据servlet-name的值找到url-pattern
                    //<servlet-name>minicat</servlet-name>
                    //<url-pattern>/minicat</url-pattern>
                    Node servletMapping = rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                    String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                    //找到LagouServlet
                    MyClassLoader myClassLoader = new MyClassLoader(host.getAppBase()+"/"+context.getName()+"/server/LagouServlet.class");
                    Class<?> c = myClassLoader.findClass(servletClass);
                    //判断是否为HttpServlet子类,且不是抽象类
                    if (Servlet.class.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers())){
                        Object o = c.newInstance();
                        String[] servletStrs = servletClass.split("\\.");
                        String servletUrl = urlPattern+"/"+(servletStrs[servletStrs.length-1].split("Servlet")[0]).toLowerCase();
                        Wrapper wrapper = new Wrapper();
                        wrapper.getServletMap().put(servletUrl,(Servlet) o);
                        context.getWrappers().add(wrapper);
                    }
                }
            }catch (DocumentException | ClassNotFoundException e){
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }


    }

    public static void main(String[] args) throws Exception {
        MainStart mainStart = new MainStart();
        mainStart.start();
    }
}
